package org.jeecg.modules.online.desform.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.online.desform.excel.converter.ConvertUtil;
import org.jeecg.modules.online.desform.util.convert.ConvertItem;
import org.jeecg.modules.online.desform.vo.excel.DesformWidgetList;
import org.jeecg.modules.online.desform.vo.widget.DesformWidget;
import org.jeecgframework.poi.util.PoiPublicUtil;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Excel 导入 处理类
 */
@Slf4j
public class DesformImportExcelHandler {

    public static final String KEY_SUB_FIELD_SPLIT = "_";

    /**
     * 子表【表名】Map，key=subName，value=subKey
     */
    private Map<String, String> subNameKeyMap = new HashMap<>();

    /**
     * 最终保存到数据库中的数据
     */
    public List<JSONObject> saveData;

    /**
     * 转换器
     */
    private Map<String, ConvertItem> converters;

    public DesformImportExcelHandler(DesformWidgetList widgetList, List<Map<String, Object>> importDataList, Map<String, ConvertItem> converters) {
        this.converters = converters;

        // 主表【字段名】Map
        Map<String, DesformWidget> mainFieldNameKeyMap = this.getNameKeyMap(widgetList.main);

        // 子表【字段名】Map，key=subKey
        Map<String, Map<String, DesformWidget>> subFieldKeyMap = new HashMap<>();
        for (String subKey : widgetList.sub.keySet()) {
            String subName = widgetList.subNames.get(subKey);
            List<DesformWidget> subList = widgetList.sub.get(subKey);

            Map<String, DesformWidget> subFieldNameKeyMap = this.getNameKeyMap(subList);
            this.subNameKeyMap.put(subName, subKey);
            subFieldKeyMap.put(subKey, subFieldNameKeyMap);
        }
        // 处理成保存到数据库里的数据
        List<JSONObject> saveData = new ArrayList<>();

        JSONObject rowData = new JSONObject();
        Map<String, JSONObject> subDataMap = new HashMap<>();
        boolean isFirst = true;

        /* 导入数据是按行导入，如果一行里没有主表字段，那么他就是上一条主表的附表。一但出现主表字段，则代表是一条新的数据 */

        // 行号，每条数据，包括主子表，一共占的行数
        int rowNum = 0;
        // 用 JSON.parseArray 处理一遍，可以去除掉字段中为 null 的值
        List<JSONObject> importDataJSON = JSON.parseArray(JSON.toJSONString(importDataList)).toJavaList(JSONObject.class);
        // 将数据处理成可识别的格式
        for (int i = 0; i < importDataJSON.size(); i++, rowNum++) {
            JSONObject importData = importDataJSON.get(i);
            // 判断该行数据是否存在主表字段
            boolean findMainField = false;
            for (String originName : importData.keySet()) {
                if (mainFieldNameKeyMap.get(originName) != null) {
                    findMainField = true;
                    break;
                }
            }
            // 如果找到了主表字段，那么就新开一行
            if (findMainField) {
                if (!isFirst) {
                    rowData = new JSONObject();
                    subDataMap = new HashMap<>();
                }
                saveData.add(rowData);
                rowNum = 1;
            }
            isFirst = false;
            // 遍历该行所有的数据
            for (String originName : importData.keySet()) {
                Object originValue = importData.get(originName);
                // 判断是否是主表字段
                DesformWidget widget = mainFieldNameKeyMap.get(originName);
                if (widget != null) {
                    // 进行数据转换
                    Object value = this.conventValue(widget.getKey(), originValue);
                    rowData.put(widget.getModel(), value);

                } else {
                    // 不是主表字段，处理子表
                    String[] keyAndName = this.getSubTableKeyAndSubFieldName(originName);
                    if (keyAndName != null) {
                        String subTableKey = keyAndName[0];
                        String subFieldName = keyAndName[1];
                        // 将子表数据添加到主表中
                        JSONArray subDataList = (JSONArray) rowData.computeIfAbsent(subTableKey, k -> new JSONArray());
                        // 存储每行的子表数据
                        JSONObject subData = subDataMap.computeIfAbsent(subTableKey + rowNum, v -> new JSONObject());


                        if (subDataList.size() < rowNum) {
                            subDataList.add(subData);
                        }

                        Map<String, DesformWidget> subWidgetName = subFieldKeyMap.get(subTableKey);
                        DesformWidget subWidget = subWidgetName.get(subFieldName);
                        // 进行数据转换
                        Object value = this.conventValue(subWidget.getKey(), originValue);
                        subData.put(subWidget.getModel(), value);
                    }
                }
            }
        }

        // 调用翻译转换方法
        List<Map<String, Object>> convertMapDataList = saveData.stream().map(row -> {
            // 翻译该行内的附表
            for (String subKey : widgetList.sub.keySet()) {

                System.out.println("row.getJSONArray(subKey): " + row.getJSONArray(subKey));
                JSONArray subJSONArray = row.getJSONArray(subKey);
                String subDataStr = "[]";
                if (subJSONArray != null) {
                    List<Map<String, Object>> subData = subJSONArray.stream().map(v -> (((JSONObject) v).getInnerMap())).collect(Collectors.toList());
                    ConvertUtil.doConvertDataList(ConvertUtil.in, subData, widgetList.sub.get(subKey));
                    subDataStr = JSON.toJSONString(subData);
                }
                // 子表需要进行 URLEncoder 编码
                try {
                    row.put(subKey, URLEncoder.encode(subDataStr, "UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }
            return row.getInnerMap();
        }).collect(Collectors.toList());
        // 翻译主表
        ConvertUtil.doConvertDataList(ConvertUtil.in, convertMapDataList, widgetList.main);

        this.saveData = saveData;
    }

    /**
     * 获取以 Name 作为Key的Map
     *
     * @param list
     * @return
     */
    private Map<String, DesformWidget> getNameKeyMap(List<DesformWidget> list) {
        Map<String, DesformWidget> map = new HashMap<>();
        list.forEach(item -> {
            map.put(item.getName(), item);
        });
        return map;
    }


    private Object conventValue(String originKey, Object originValue) {
        // 转换Value的值
        Object value;
        if (originValue instanceof Double) {
            value = PoiPublicUtil.doubleToString((Double) originValue);
        } else {
            value = originValue == null ? "" : originValue.toString();
        }
        ConvertItem convertItem = this.converters.get(originKey);
        if (convertItem != null) {
            // 执行转换操作
            value = convertItem.doConvert(value);
        }
        return value;
    }

    /**
     * 通过【子表名_字段名】获取子表名和字段名
     *
     * @return StringArray； 0=子表Key; 1=字段名
     */
    public String[] getSubTableKeyAndSubFieldName(String originName) {
        String[] splitArray = originName.split(KEY_SUB_FIELD_SPLIT);

        if (splitArray.length >= 2) {
            String subTableName = splitArray[0];
            // 拼接subFieldName，除了index=0是子表表名之外，剩下的都是子表字段名
            StringBuilder subFieldNameSB = new StringBuilder(splitArray[1]);
            for (int i = 2; i < splitArray.length; i++) {
                subFieldNameSB.append(KEY_SUB_FIELD_SPLIT).append(splitArray[i]);
            }
            // 获取subTableKey和subFieldNameMap
            String subTableKey = this.subNameKeyMap.get(subTableName);
            String subFieldName = subFieldNameSB.toString();
            if (subTableKey != null) {
                return new String[]{subTableKey, subFieldName};
            } else {
                log.error("导入时遇到不存在的子表——originName：" + originName + "；subTableName：" + subTableName);
            }
        } else {
            log.error("导入时遇到无法解析的——originName：" + originName);
        }
        return null;
    }

}